﻿using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;

namespace Hont.AStar
{
    [CanEditMultipleObjects]
    [CustomEditor(typeof(HontAStarUnityDebugNode))]
    public class HontAStarUnityDebugNode_Inspector : Editor
    {
        IHontAStarUnityMaskCustomEnum mMappingEnum;
        bool? mHasMappingEnum;
        bool mIsEditGrow;
        bool mIsEditGrowInit;

        Vector3 mMinLengthFreeHandle;
        Vector3 mMaxLengthFreeHandle;

        Vector3 mMinHeightFreeHandle;
        Vector3 mMaxHeightFreeHandle;

        Vector3 mMinWidthFreeHandle;
        Vector3 mMaxWidthFreeHandle;


        void OnSceneGUI()
        {
            if (!mIsEditGrow) return;

            GrowSceneGUIRefresh();
        }

        public override void OnInspectorGUI()
        {
            HotKeyMoveCubeDetection();
            HotKeyFunctionDetection();

            if (base.targets.Length > 1 && GUILayout.Button(i18N.SEL_MAPPING_POINT))
            {
                SelectionMappingPoint();
            }

            mIsEditGrow = GUILayout.Toggle(mIsEditGrow, i18N.EDIT_GROW, GUI.skin.button);

            if (!mIsEditGrow && mIsEditGrowInit)
            {
                SceneView.RepaintAll();
                mIsEditGrowInit = false;
            }
            else if (mIsEditGrow && !mIsEditGrowInit)
            {
                SceneView.RepaintAll();
            }

            GUILayout.BeginVertical(GUI.skin.box);
            if (GUILayout.Button(i18N.SEL_EQULS_MASK))
            {
                SelectionEqulsMask();
            }

            if (GUILayout.Button(i18N.SEL_EQULS_COST))
            {
                SelectionEqulsCost();
            }

            if (GUILayout.Button(i18N.SEL_TO_ASTAR_ROOT))
            {
                var conv = base.target as HontAStarUnityDebugNode;
                Selection.activeGameObject = conv.host.gameObject;
            }
            GUILayout.EndVertical();

            EditorGUILayout.HelpBox("Press hot-key 'w,a,s,d,q,e' you can easily move the index", MessageType.Info);

            base.OnInspectorGUI();

            CustomEnumObject();
        }

        TInterface GetAssemblyInterfaceAndCreate<TInterface>()
        {
            var ass = Assembly.GetAssembly(typeof(TInterface));

            var targetType = ass.GetTypes()
                .Where(m => m.GetInterface(typeof(TInterface).FullName) != null)
                .Where(m => !m.IsAbstract && !m.IsInterface).FirstOrDefault();

            if (targetType == null) return default(TInterface);

            var instance = (TInterface)ass.CreateInstance(targetType.FullName);

            return instance;
        }

        void CustomEnumObject()
        {
            if (base.targets.Length > 0)
            {
                var firstValue = base.targets[0];

                var host = firstValue as HontAStarUnityDebugNode;

                if (mHasMappingEnum == false) return;

                if (mMappingEnum == null && mHasMappingEnum.GetValueOrDefault(true))
                {
                    mMappingEnum = GetAssemblyInterfaceAndCreate<IHontAStarUnityMaskCustomEnum>();

                    if (mMappingEnum == null) mHasMappingEnum = false;
                }

                if (mMappingEnum != null)
                {
                    var type = Enum.ToObject(mMappingEnum.MaskEnum.GetType(), host.mask);

                    var targetValue = Convert.ToInt32(EditorGUILayout.EnumPopup(new GUIContent("Mask"), (Enum)type));

                    for (int i = 0; i < base.targets.Length; i++)
                    {
                        var item = base.targets[i] as HontAStarUnityDebugNode;

                        item.mask = targetValue;
                    }
                }
            }
        }

        void SelectionMappingPoint()
        {
            var newSelectionList = new List<GameObject>();

            foreach (var item in targets)
            {
                if (!(item is HontAStarUnityDebugNode)) continue;

                var node = item as HontAStarUnityDebugNode;

                newSelectionList.Add(node.customMappingTransform.gameObject);
            }

            Selection.objects = newSelectionList.ToArray();

            Tools.current = Tool.Move;
        }

        void SelectionEqulsMask()
        {
            var conv = base.target as HontAStarUnityDebugNode;
            var astar = conv.host;

            if (astar == null) return;
            if (astar.Grid == null) return;

            var newSelectionList = new List<GameObject>();

            foreach (Position item in astar.Grid)
            {
                var node = astar.Grid.GetUserData(item) as HontAStarUnityDebugNode;

                if (node.mask == conv.mask)
                    newSelectionList.Add(node.gameObject);
            }

            Selection.objects = newSelectionList.ToArray();
        }

        void SelectionEqulsCost()
        {
            var conv = base.target as HontAStarUnityDebugNode;
            var astar = conv.host;

            if (astar == null) return;
            if (astar.Grid == null) return;

            var newSelectionList = new List<GameObject>();

            foreach (Position item in astar.Grid)
            {
                var node = astar.Grid.GetUserData(item) as HontAStarUnityDebugNode;

                if (node.cost == conv.cost)
                    newSelectionList.Add(node.gameObject);
            }

            Selection.objects = newSelectionList.ToArray();
        }

        void GrowSceneGUIRefresh()
        {
            var host = base.target as HontAStarUnityDebugNode;
            var bounds = default(Bounds);

            if (!mIsEditGrowInit)
            {
                bounds = host.Bounds;
                mMinLengthFreeHandle = bounds.center;
                mMaxLengthFreeHandle = bounds.center;
                mMinHeightFreeHandle = bounds.center;
                mMaxHeightFreeHandle = bounds.center;
                mMinWidthFreeHandle = bounds.center;
                mMaxWidthFreeHandle = bounds.center;

                mMinLengthFreeHandle.x = bounds.min.x;
                mMaxLengthFreeHandle.x = bounds.max.x;

                mMinHeightFreeHandle.y = bounds.min.y;
                mMaxHeightFreeHandle.y = bounds.max.y;

                mMinWidthFreeHandle.z = bounds.min.z;
                mMaxWidthFreeHandle.z = bounds.max.z;

                mIsEditGrowInit = true;
            }

            mMinLengthFreeHandle = Handles.FreeMoveHandle(mMinLengthFreeHandle, Quaternion.identity, HandleUtility.GetHandleSize(mMinLengthFreeHandle) * 0.05f, Vector3.zero, Handles.DotCap);
            mMaxLengthFreeHandle = Handles.FreeMoveHandle(mMaxLengthFreeHandle, Quaternion.identity, HandleUtility.GetHandleSize(mMaxLengthFreeHandle) * 0.05f, Vector3.zero, Handles.DotCap);

            mMinHeightFreeHandle = Handles.FreeMoveHandle(mMinHeightFreeHandle, Quaternion.identity, HandleUtility.GetHandleSize(mMinHeightFreeHandle) * 0.05f, Vector3.zero, Handles.DotCap);
            mMaxHeightFreeHandle = Handles.FreeMoveHandle(mMaxHeightFreeHandle, Quaternion.identity, HandleUtility.GetHandleSize(mMaxHeightFreeHandle) * 0.05f, Vector3.zero, Handles.DotCap);

            mMinWidthFreeHandle = Handles.FreeMoveHandle(mMinWidthFreeHandle, Quaternion.identity, HandleUtility.GetHandleSize(mMinWidthFreeHandle) * 0.05f, Vector3.zero, Handles.DotCap);
            mMaxWidthFreeHandle = Handles.FreeMoveHandle(mMaxWidthFreeHandle, Quaternion.identity, HandleUtility.GetHandleSize(mMaxWidthFreeHandle) * 0.05f, Vector3.zero, Handles.DotCap);

            bounds = host.Bounds;

            mMinLengthFreeHandle.y = bounds.center.y;
            mMinLengthFreeHandle.z = bounds.center.z;
            mMaxLengthFreeHandle.y = bounds.center.y;
            mMaxLengthFreeHandle.z = bounds.center.z;

            mMinHeightFreeHandle.x = bounds.center.x;
            mMinHeightFreeHandle.z = bounds.center.z;
            mMaxHeightFreeHandle.x = bounds.center.x;
            mMaxHeightFreeHandle.z = bounds.center.z;

            mMinWidthFreeHandle.x = bounds.center.x;
            mMinWidthFreeHandle.y = bounds.center.y;
            mMaxWidthFreeHandle.x = bounds.center.x;
            mMaxWidthFreeHandle.y = bounds.center.y;

            var min = new Vector3(mMinLengthFreeHandle.x, mMinHeightFreeHandle.y, mMinWidthFreeHandle.z);
            var max = new Vector3(mMaxLengthFreeHandle.x, mMaxHeightFreeHandle.y, mMaxWidthFreeHandle.z);
            var growBounds = new Bounds() { min = min, max = max };

            DrawWireCube(growBounds.center, growBounds.size);

            if (Event.current.type == EventType.MouseUp)
            {
                var childList = new List<HontAStarUnityDebugNode>();

                foreach (Transform item in host.host.transform)
                {
                    childList.Add(item.GetComponent<HontAStarUnityDebugNode>());
                }

                var changedBox = childList
                    .Where(m => m.isWalkable != host.isWalkable)
                    .Where(m => growBounds.Contains(m.Bounds.center));

                foreach (var item in changedBox)
                {
                    item.isWalkable = host.isWalkable;
                }
            }
        }

        void HotKeyMoveCubeDetection()
        {
            var host = base.target as HontAStarUnityDebugNode;
            var astar = host.host;

            if (astar == null) return;
            if (astar.Grid == null) return;

            var target = default(HontAStarUnityDebugNode);
            try
            {
                target = astar.Grid.GetUserData(host.AStarPosition) as HontAStarUnityDebugNode;
            }
            catch { }

            if (target == null) return;

            var flag = false;
            var offset = new Position();

            switch (Event.current.keyCode)
            {
                case KeyCode.W:
                    offset.Z = 1;
                    flag = true;
                    break;
                case KeyCode.S:
                    offset.Z = -1;
                    flag = true;
                    break;
                case KeyCode.A:
                    offset.X = -1;
                    flag = true;
                    break;
                case KeyCode.D:
                    offset.X = 1;
                    flag = true;
                    break;
                case KeyCode.Q:
                    offset.Y = 1;
                    flag = true;
                    break;
                case KeyCode.E:
                    offset.Y = -1;
                    flag = true;
                    break;
            }

            if (flag && Event.current.type == EventType.KeyUp)
            {
                var finalPos = new Position(target.astar_x + offset.X, target.astar_y + offset.Y, target.astar_z + offset.Z);

                finalPos.X = Mathf.Clamp(finalPos.X, 0, astar.Grid.Lenght - 1);
                finalPos.Y = Mathf.Clamp(finalPos.Y, 0, astar.Grid.Height - 1);
                finalPos.Z = Mathf.Clamp(finalPos.Z, 0, astar.Grid.Width - 1);

                var userData = astar.Grid.GetUserData(finalPos);

                var node = userData as HontAStarUnityDebugNode;

                Selection.activeGameObject = node.gameObject;
            }
        }

        void HotKeyFunctionDetection()
        {
            if (Event.current.type != EventType.KeyUp) return;

            var host = base.target as HontAStarUnityDebugNode;

            if (Event.current.keyCode == KeyCode.I)
            {
                host.isWalkable = !host.isWalkable;
            }
        }

        void DrawWireCube(Vector3 position, Vector3 size)
        {
            Vector3 half = size * 0.5f;

            Vector3 v1 = position + new Vector3(half.x, half.y, half.z);
            Vector3 v2 = position + new Vector3(half.x, half.y, -half.z);
            Vector3 v3 = position + new Vector3(half.x, -half.y, half.z);
            Vector3 v4 = position + new Vector3(half.x, -half.y, -half.z);
            Vector3 v5 = position + new Vector3(-half.x, half.y, half.z);
            Vector3 v6 = position + new Vector3(-half.x, half.y, -half.z);
            Vector3 v7 = position + new Vector3(-half.x, -half.y, half.z);
            Vector3 v8 = position + new Vector3(-half.x, -half.y, -half.z);

            Handles.DrawLine(v7, v3);
            Handles.DrawLine(v7, v5);
            Handles.DrawLine(v1, v3);
            Handles.DrawLine(v1, v5);
            Handles.DrawLine(v8, v4);
            Handles.DrawLine(v8, v6);
            Handles.DrawLine(v2, v4);
            Handles.DrawLine(v2, v6);
            Handles.DrawLine(v8, v7);
            Handles.DrawLine(v4, v3);
            Handles.DrawLine(v6, v5);
            Handles.DrawLine(v2, v1);
        }
    }
}
